/***************************************************
 @Name :        1196kk0
 @Time :        2025-05-26
 @Email :       2232506563@qq.com
 @Comment :     软件模拟IIC
 @FileName :    drv_IIC.c
 @Version :     4.0
****************************************************/

#include "drv_IIC.h"
#include "drivers_Systick.h"

/**************************************************
函数名称：drv_IIC_Config
函数功能：IIC初始化函数
输入参数：无
输出参数：无
其它说明：//pb6->SCL
		  //pb7->SDA
***************************************************/
void drv_IIC_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB , ENABLE);

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB , &GPIO_InitStruct);
}

/**************************************************
函数名称：drv_IIC_in
函数功能：将SDA数据线设置为输入
输入参数：无
输出参数：无
其它说明：//pb6->SCL
		  //pb7->SDA
***************************************************/
void drv_IIC_in(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB , &GPIO_InitStruct);
}

/**************************************************
函数名称：drv_IIC_out
函数功能：将SDA数据线设置为输出
输入参数：无
输出参数：无
其它说明：//pb6->SCL
		  //pb7->SDA
***************************************************/
void drv_IIC_out(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB , &GPIO_InitStruct);
}

/**************************************************
函数名称：drv_IIC_start
函数功能：模拟IIC起始信号
输入参数：无
输出参数：无
其它说明：当时钟线为高电平时，数据线由高电平变为低电平，产生一个有效的下降沿
***************************************************/
void drv_IIC_start(void)
{
    SCL_L;          //拉低时钟线，使数据无效
    drv_IIC_out();  //将数据线设置为输出模式

    SDA_H;          //拉高数据线
    SCL_H;          //拉高时钟线，使数据有效
    delay_us(5);    //延时5us，满足IIC时序要求
    SDA_L;          //拉低数据线，产生下降沿
    delay_us(5);    //延时5us，满足IIC时序要求
    SCL_L;          //拉低时钟线占用总线
}

/**************************************************
函数名称：drv_IIC_end
函数功能：模拟IIC停止信号
输入参数：无
输出参数：无
其它说明：当时钟线为高电平时，数据线由低电平变为高电平，产生一个有效的上升沿
***************************************************/
void drv_IIC_end(void)
{
    SCL_L;          //拉低时钟线，使数据无效
    drv_IIC_out();  //将数据线设置为输出模式
    SDA_L;          //拉低数据线
    SCL_H;          //拉高时钟线，使数据有效
    delay_us(5);    //延时5us，满足IIC时序要求
    SDA_H;          //拉高数据线，产生上升沿
    delay_us(5);    //延时5us，满足IIC时序要求
                    //不用拉低时钟线，不再占用总线
}

/**************************************************
函数名称：drv_IIC_sendACk
函数功能：模拟IIC发送应答信号
输入参数：无
输出参数：无
其它说明：当时钟线为高电平时，数据线产生一个有效的低电平
***************************************************/
void drv_IIC_sendACk(void)
{
    SCL_L;          //拉低时钟线，使数据无效
    drv_IIC_out();  //将数据线设置为输出模式

    SDA_L;          //拉低数据线
    SCL_H;          //拉高时钟线，使数据有效
    delay_us(5);    //延时5us，满足IIC时序要求
    SCL_L;          //拉低时钟线占用总线
}

/**************************************************
函数名称：drv_IIC_sendNoACk
函数功能：模拟IIC发送非应答信号
输入参数：无
输出参数：无
其它说明：当时钟线为高电平时，数据线产生一个有效的高电平
***************************************************/
void drv_IIC_sendNoACk(void)
{
    SCL_L;          //拉低时钟线，使数据无效
    drv_IIC_out();  //将数据线设置为输出模式

    SDA_H;          //拉高数据线
    SCL_H;          //拉高时钟线，使数据有效
    delay_us(5);    //延时5us，满足IIC时序要求
    SCL_L;          //拉低时钟线占用总线
}

/**************************************************
函数名称：drv_IIC_waitACK
函数功能：模拟IIC等待应答信号
输入参数：无
输出参数：根据读取的结果返回ACK或者NOACK
其它说明：把时钟线拉高，读取数据线上的数据：高电平（非应答）、低电平（应答）
***************************************************/
uint8_t drv_IIC_waitACK(void)
{
    uint16_t tim_out = 0;

    SCL_L;          //拉低时钟线，使数据无效
    drv_IIC_in();   //将数据线设置为输入模式

    SCL_H;          //拉高时钟线，使数据有效
    while (READ_SDA)
    {
        tim_out++;
        if (tim_out > 500)  //超时没有等到应答信号停止
        {
            drv_IIC_end();
            return NOACK;
        }
    }
    SCL_L;          //拉低时钟线占用总线
    return ACK;
}

/**************************************************
函数名称：drv_IIC_sendData

函数功能：模拟IIC发送一个字节数据

输入参数：data：需要发送的数据

输出参数：无

其它说明：
***************************************************/
void drv_IIC_sendData(uint8_t data)
{
    uint8_t i = 0;
    SCL_L;          //拉低时钟线，使数据无效
    drv_IIC_out();  //将数据线设置为输出模式

    for ( i = 0; i < 8 ; i++)
    {
        if (data & 0x80)
        {
            SDA_H;
        }
        else
        {
            SDA_L;
        }
        SCL_H;       //将时钟线拉高，使数据有效
        delay_us(1);
        SCL_L;
        delay_us(1);
        data = data << 1;
    }
}

/**************************************************
函数名称：drv_IIC_readData

函数功能：模拟IIC读取一个字节数据

输入参数：flg_ack：发送应答/非应答信号标志

输出参数：read_data：读取的数据

其它说明：把时钟线拉高，获取数据线上的数据，接收完数据以后必须要把时钟线拉低，允许从机发送下一个位的数据
***************************************************/
uint8_t drv_IIC_readData(uint8_t flg_ack)
{
    uint8_t read_data = 0 , i = 0;
    SCL_L;          //拉低时钟线，使数据无效
    drv_IIC_in();   //将数据线设置为输入模式

    for (i = 0; i < 8; i++)
    {
        read_data = read_data << 1;
        SCL_H;
        if (READ_SDA)
        {
            read_data |= 0x01;
        }
        delay_us(1);
        SCL_L;
        delay_us(1);
    }
    if (flg_ack == ACK)
    {
        drv_IIC_sendACk();
    }
    else
    {
        drv_IIC_sendNoACk();
    }
    SCL_L;
    return read_data;
}


